#ifndef GLB_H_GUARD
#define GLB_H_GUARD

// #if !(CUPDLP_CPU)
// #include <cublas_v2.h>         // cublas
// #include <cuda_runtime_api.h>  // cudaMalloc, cudaMemcpy, etc.
// #include <cusparse.h>          // cusparseSpMV
// #endif

#ifdef __cplusplus

extern "C" {
#endif

#include <math.h>

// return code
#define RETCODE_OK (0)
#define RETCODE_FAILED (1)
#define BOOL (1)
// #define DLONG

#ifndef cupdlp
#define cupdlp(x) cupdlp_##x
#endif

/* cupdlp VERSION NUMBER ----------------------------------------------    */
#define cupdlp_VERSION \
  ("1.0.0") /* string literals automatically null-terminated */

#ifdef MATLAB_MEX_FILE
#include "mex.h"
#define cupdlp_printf mexPrintf
#define _cupdlp_free mxFree
#define _cupdlp_malloc mxMalloc
#define _cupdlp_calloc mxCalloc
#define _cupdlp_realloc mxRealloc
#elif defined PYTHON
#include <Python.h>
#include <stdlib.h>
#define cupdlp_printf(...)                           \
  {                                                  \
    PyGILState_STATE gilstate = PyGILState_Ensure(); \
    PySys_WriteStdout(__VA_ARGS__);                  \
    PyGILState_Release(gilstate);                    \
  }
#define _cupdlp_free free
#define _cupdlp_malloc malloc
#define _cupdlp_calloc calloc
#define _cupdlp_realloc realloc
#else

#include <stdio.h>
#include <stdlib.h>

#define cupdlp_printf printf
#define cupdlp_snprintf snprintf
#define _cupdlp_free free
#define _cupdlp_malloc malloc
#define _cupdlp_calloc calloc
#define _cupdlp_realloc realloc
#endif

// for cuda
#if !(CUPDLP_CPU)

// #define CUPDLP_FREE_VEC(x) \
//   {                        \
//     cudaFree(x);           \
//     x = cupdlp_NULL;       \
//   }

// #define CUPDLP_COPY_VEC(dst, src, type, size) \
//   cudaMemcpy(dst, src, sizeof(type) * (size), cudaMemcpyDefault)

// #define CUPDLP_INIT_VEC(var, size)                                             \
//   {                                                                            \
//     cusparseStatus_t status =                                                  \
//         cudaMalloc((void **)&var, (size) * sizeof(__typeof__(*var)));          \
//     if (status != CUSPARSE_STATUS_SUCCESS) {                                   \
//       printf("CUSPARSE API failed at line %d with error: %s (%d)\n", __LINE__, \
//              cusparseGetErrorString(status), status);                          \
//       goto exit_cleanup;                                                       \
//     }                                                                          \
//   }
// #define CUPDLP_INIT_ZERO_VEC(var, size)                                        \
//   {                                                                            \
//     cusparseStatus_t status =                                                  \
//         cudaMalloc((void **)&var, (size) * sizeof(__typeof__(*var)));          \
//     if (status != CUSPARSE_STATUS_SUCCESS) {                                   \
//       printf("CUSPARSE API failed at line %d with error: %s (%d)\n", __LINE__, \
//              cusparseGetErrorString(status), status);                          \
//       goto exit_cleanup;                                                       \
//     }                                                                          \
//     status = cudaMemset(var, 0, (size) * sizeof(__typeof__(*var)));            \
//     if (status != CUSPARSE_STATUS_SUCCESS) {                                   \
//       printf("CUSPARSE API failed at line %d with error: %s (%d)\n", __LINE__, \
//              cusparseGetErrorString(status), status);                          \
//       goto exit_cleanup;                                                       \
//     }                                                                          \
//   }
// #define CUPDLP_ZERO_VEC(var, type, size) \
//   cudaMemset(var, 0, sizeof(type) * (size))

#else
#define CUPDLP_COPY_VEC(dst, src, type, size) \
  memcpy(dst, src, sizeof(type) * (size))
#define CUPDLP_INIT_VEC(var, size)                                      \
  {                                                                     \
    (var) = (__typeof__(var))malloc((size) * sizeof(__typeof__(*var))); \
    if ((var) == cupdlp_NULL) {                                         \
      retcode = RETCODE_FAILED;                                         \
      goto exit_cleanup;                                                \
    }                                                                   \
  }
#define CUPDLP_INIT_ZERO_VEC(var, size)                              \
  {                                                                  \
    (var) = (__typeof__(var))calloc(size, sizeof(__typeof__(*var))); \
    if ((var) == cupdlp_NULL) {                                      \
      retcode = RETCODE_FAILED;                                      \
      goto exit_cleanup;                                             \
    }                                                                \
  }
#define CUPDLP_FREE_VEC(x) \
  {                        \
    _cupdlp_free(x);       \
    x = cupdlp_NULL;       \
  }
#define CUPDLP_ZERO_VEC(var, type, size) memset(var, 0, sizeof(type) * (size))

#endif

#define cupdlp_free(x) \
  {                    \
    _cupdlp_free(x);   \
    x = cupdlp_NULL;   \
  }
#define cupdlp_malloc(x) _cupdlp_malloc(x)
#define cupdlp_calloc(x, y) _cupdlp_calloc(x, y)
#define cupdlp_realloc(x, y) _cupdlp_realloc(x, y)
#define cupdlp_zero(var, type, size) memset(var, 0, sizeof(type) * (size))
#define cupdlp_copy(dst, src, type, size) \
  memcpy(dst, src, sizeof(type) * (size))
#define CUPDLP_INIT(var, size)                                          \
  {                                                                     \
    (var) = (__typeof__(var))malloc((size) * sizeof(__typeof__(*var))); \
    if ((var) == cupdlp_NULL) {                                         \
      retcode = RETCODE_FAILED;                                         \
      goto exit_cleanup;                                                \
    }                                                                   \
  }
#define CUPDLP_INIT_ZERO(var, size)                                  \
  {                                                                  \
    (var) = (__typeof__(var))calloc(size, sizeof(__typeof__(*var))); \
    if ((var) == cupdlp_NULL) {                                      \
      retcode = RETCODE_FAILED;                                      \
      goto exit_cleanup;                                             \
    }                                                                \
  }
#define CUPDLP_FREE(var) cupdlp_free(var)
#define CUPDLP_CALL(func)       \
  {                             \
    if ((func) != RETCODE_OK) { \
      retcode = RETCODE_FAILED; \
      goto exit_cleanup;        \
    }                           \
  }

#ifndef SFLOAT
#ifdef DLONG
typedef long long cupdlp_int;
#else
typedef int cupdlp_int;
#endif
typedef double cupdlp_float;
#ifndef NAN
#define NAN ((cupdlp_float)0x7ff8000000000000)
#endif
#ifndef INFINITY
#define INFINITY NAN
#endif
#else
typedef float cupdlp_float;
#ifndef NAN
#define NAN ((float)0x7fc00000)
#endif
#ifndef INFINITY
#define INFINITY NAN
#endif
#endif

#ifdef BOOL

#include <stdbool.h>

typedef bool cupdlp_bool;
#endif

#define cupdlp_NULL 0

#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif

#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif

#ifndef ABS
#define ABS(x) (((x) < 0) ? -(x) : (x))
#endif

#ifndef POWF
#ifdef SFLOAT
#define POWF powf
#else
#define POWF pow
#endif
#endif

#ifndef SQRTF
#ifdef SFLOAT
#define SQRTF sqrtf
#else
#define SQRTF sqrt
#endif
#endif

#if EXTRA_VERBOSE > 1
#if (defined _WIN32 || defined _WIN64 || defined _WINDLL)
#define __func__ __FUNCTION__
#endif
#define DEBUG_FUNC                                                     \
  cupdlp_printf("IN function: %s, time: %4f ms, file: %s, line: %i\n", \
                __func__, cupdlp(tocq)(&global_timer), __FILE__, __LINE__);
#define RETURN
cupdlp_printf("EXIT function: %s, time: %4f ms, file: %s, line: %i\n", __func__,
              cupdlp(tocq)(&global_timer), __FILE__, __LINE__);
return
#else
#define DEBUG_FUNC
#define RETURN return
#endif

#define EPS_TOL (1E-18)
#define EPS (1E-8)  // for condition number in subnp
#define SAFEDIV_POS(X, Y) ((Y) < EPS_TOL ? ((X) / EPS_TOL) : (X) / (Y))

#define CONVERGED_INTERVAL (1)
#define INDETERMINATE_TOL (1e-9)

#define OUR_DBL_MAX 1E+20

#ifndef CUPDLP_ASSERT_H

#include <assert.h>

#define CUPDLP_ASSERT assert
#endif

#ifdef __cplusplus
}
#endif
#endif
